#ifndef __OCPP_Cesium_Cartesian3_H__
#define __OCPP_Cesium_Cartesian3_H__

#include "Spherical.h"
#include "CesiumMath.h"

namespace OCPP
{
	namespace Cesium
	{
		class _CesiumExport Cartesian3
		{
		public:
			double x, y, z;
			Cartesian3(double _x = 0.0, double _y = 0.0, double _z = 0.0)
				: x(_x), y(_y), z(_z)
			{
			}

			Cartesian3(const Cartesian3 &other)
			{
				this->x = other.x;
				this->y = other.y;
				this->z = other.z;
			}

			Cartesian3 &operator=(const Cartesian3 &fScaler)
			{
				x = fScaler.x;
				y = fScaler.y;
				z = fScaler.z;

				return (Cartesian3 &)*this;
			}

			Cartesian3 fromSpherical(Spherical spherical)
			{
				Cartesian3 result;
				double clock = spherical.clock;
				double cone = spherical.cone;
				double magnitude = spherical.magnitude;
				double radial = magnitude * sin(cone);
				result.x = radial * cos(clock);
				result.y = radial * sin(clock);
				result.z = magnitude * cos(cone);
				return result;
			};

			/**
			 * Creates a Cartesian3 instance from x, y and z coordinates.
			 *
			 * @param {number} x The x coordinate.
			 * @param {number} y The y coordinate.
			 * @param {number} z The z coordinate.
			 * @param {Cartesian3} [result] The object onto which to store the result.
			 * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided.
			 */
			static Cartesian3 fromElements(double x, double y, double z)
			{
				Cartesian3 result;
				result.x = x;
				result.y = y;
				result.z = z;
				return result;
			};

			/**
			 * Creates a Cartesian3 instance from an existing Cartesian4.  This simply takes the
			 * x, y, and z properties of the Cartesian4 and drops w.
			 * @function
			 *
			 * @param {Cartesian4} cartesian The Cartesian4 instance to create a Cartesian3 instance from.
			 * @param {Cartesian3} [result] The object onto which to store the result.
			 * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided.
			 */
			Cartesian3 fromCartesian4(Cartesian4 cartesian4);

			/**
			 * The number of elements used to pack the object into an array1.
			 * @type {number}
			 */
			inline static const int packedLength = 3;

			/**
			 * Stores the provided instance into the provided array1.
			 *
			 * @param {Cartesian3} value The value to pack.
			 * @param {number[]} array1 The array1 to pack into.
			 * @param {number} [startingIndex=0] The index into the array1 at which to start packing the elements.
			 *
			 * @returns {number[]} The array1 that was packed into
			 */
			static DoubleVector pack(Cartesian3 &value, DoubleVector array1, size_t startingIndex = 0)
			{
				array1[startingIndex++] = value.x;
				array1[startingIndex++] = value.y;
				array1[startingIndex] = value.z;

				return array1;
			};

			/**
			 * Retrieves an instance from a packed array1.
			 *
			 * @param {number[]} array1 The packed array1.
			 * @param {number} [startingIndex=0] The starting index of the element to be unpacked.
			 * @param {Cartesian3} [result] The object into which to store the result.
			 * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided.
			 */
			static Cartesian3 unpack(DoubleVector array1, size_t startingIndex = 0)
			{
				Cartesian3 result;
				result.x = array1[startingIndex++];
				result.y = array1[startingIndex++];
				result.z = array1[startingIndex];
				return result;
			};

			/**
			 * Flattens an array1 of Cartesian3s into an array1 of components.
			 *
			 * @param {Cartesian3[]} array1 The array1 of cartesians to pack.
			 * @param {number[]} [result] The array1 onto which to store the result. If this is a typed array1, it must have array1.length * 3 components, else a {@link DeveloperError} will be thrown. If it is a regular array1, it will be resized to have (array1.length * 3) elements.
			 * @returns {number[]} The packed array1.
			 */
			static DoubleVector packArray(std::vector<Cartesian3> array1)
			{
				size_t length = array1.size();
				size_t resultLength = length * 3;
				DoubleVector result;
				result.resize(resultLength);

				for (size_t i = 0; i < length; ++i)
				{
					Cartesian3::pack(array1[i], result, i * 3);
				}
				return result;
			};

			/**
			 * Unpacks an array1 of cartesian components into an array1 of Cartesian3s.
			 *
			 * @param {number[]} array1 The array1 of components to unpack.
			 * @param {Cartesian3[]} [result] The array1 onto which to store the result.
			 * @returns {Cartesian3[]} The unpacked array1.
			 */
			static std::vector<Cartesian3> unpackArray(DoubleVector array1)
			{
				size_t length = array1.size();
				std::vector<Cartesian3> result;
				result.resize(length / 3);
				for (size_t i = 0; i < length; i += 3)
				{
					size_t index = i / 3;
					result[index] = (Cartesian3::unpack(array1, i));
				}
				return result;
			};

			/**
			 * Creates a Cartesian3 from three consecutive elements in an array1.
			 * @function
			 *
			 * @param {number[]} array1 The array1 whose three consecutive elements correspond to the x, y, and z components, respectively.
			 * @param {number} [startingIndex=0] The offset into the array1 of the first element, which corresponds to the x component.
			 * @param {Cartesian3} [result] The object onto which to store the result.
			 * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided.
			 *
			 * @example
			 * // Create a Cartesian3 with (1.0, 2.0, 3.0)
			 * const v = [1.0, 2.0, 3.0];
			 * const p = Cesium.Cartesian3.fromArray(v);
			 *
			 * // Create a Cartesian3 with (1.0, 2.0, 3.0) using an offset into an array1
			 * const v2 = [0.0, 0.0, 1.0, 2.0, 3.0];
			 * const p2 = Cesium.Cartesian3.fromArray(v2, 2);
			 */
			static Cartesian3 fromArray(DoubleVector array1, size_t startingIndex = 0)
			{
				return Cartesian3::unpack(array1, startingIndex);
			}

			/**
			 * Computes the value of the maximum component for the supplied Cartesian.
			 *
			 * @param {Cartesian3} cartesian The cartesian to use.
			 * @returns {number} The value of the maximum component.
			 */
			static double maximumComponent(Cartesian3 &cartesian)
			{
				return (std::max)(cartesian.x, (std::max)(cartesian.y, cartesian.z));
			};

			/**
			 * Computes the value of the minimum component for the supplied Cartesian.
			 *
			 * @param {Cartesian3} cartesian The cartesian to use.
			 * @returns {number} The value of the minimum component.
			 */
			static double minimumComponent(Cartesian3 &cartesian)
			{
				return (std::min)(cartesian.x, (std::min)(cartesian.y, cartesian.z));
			};

			/**
			 * Compares two Cartesians and computes a Cartesian which contains the minimum components of the supplied Cartesians.
			 *
			 * @param {Cartesian3} first A cartesian to compare.
			 * @param {Cartesian3} second A cartesian to compare.
			 * @param {Cartesian3} result The object into which to store the result.
			 * @returns {Cartesian3} A cartesian with the minimum components.
			 */
			static Cartesian3 minimumByComponent(Cartesian3 &first, Cartesian3 &second)
			{
				Cartesian3 result;
				result.x = (std::min)(first.x, second.x);
				result.y = (std::min)(first.y, second.y);
				result.z = (std::min)(first.z, second.z);

				return result;
			};

			/**
			 * Compares two Cartesians and computes a Cartesian which contains the maximum components of the supplied Cartesians.
			 *
			 * @param {Cartesian3} first A cartesian to compare.
			 * @param {Cartesian3} second A cartesian to compare.
			 * @param {Cartesian3} result The object into which to store the result.
			 * @returns {Cartesian3} A cartesian with the maximum components.
			 */
			static Cartesian3 maximumByComponent(Cartesian3 &first, Cartesian3 &second)
			{
				Cartesian3 result;
				result.x = (std::max)(first.x, second.x);
				result.y = (std::max)(first.y, second.y);
				result.z = (std::max)(first.z, second.z);
				return result;
			};

			/**
			 * Constrain a value to lie between two values.
			 *
			 * @param {Cartesian3} cartesian The value to clamp.
			 * @param {Cartesian3} min The minimum bound.
			 * @param {Cartesian3} max The maximum bound.
			 * @param {Cartesian3} result The object into which to store the result.
			 * @returns {Cartesian3} The clamped value such that min <= value <= max.
			 */
			static Cartesian3 clamp(Cartesian3 &value, Cartesian3 &min, Cartesian3 &max)
			{
				double x = CesiumMath::clamp(value.x, min.x, max.x);
				double y = CesiumMath::clamp(value.y, min.y, max.y);
				double z = CesiumMath::clamp(value.z, min.z, max.z);

				Cartesian3 result;
				result.x = x;
				result.y = y;
				result.z = z;

				return result;
			};

			/**
			 * Computes the provided Cartesian's squared magnitude.
			 *
			 * @param {Cartesian3} cartesian The Cartesian instance whose squared magnitude is to be computed.
			 * @returns {number} The squared magnitude.
			 */
			static double magnitudeSquared(Cartesian3 &cartesian)
			{
				return (
					cartesian.x * cartesian.x +
					cartesian.y * cartesian.y +
					cartesian.z * cartesian.z);
			};

			/**
			 * Computes the Cartesian's magnitude (length).
			 *
			 * @param {Cartesian3} cartesian The Cartesian instance whose magnitude is to be computed.
			 * @returns {number} The magnitude.
			 */
			static double magnitude(Cartesian3 cartesian)
			{
				return sqrt(Cartesian3::magnitudeSquared(cartesian));
			};

			/**
			 * Computes the distance between two points.
			 *
			 * @param {Cartesian3} left The first point to compute the distance from.
			 * @param {Cartesian3} right The second point to compute the distance to.
			 * @returns {number} The distance between two points.
			 *
			 * @example
			 * // Returns 1.0
			 * const d = Cesium.Cartesian3.distance(new Cesium.Cartesian3(1.0, 0.0, 0.0), new Cesium.Cartesian3(2.0, 0.0, 0.0));
			 */
			static double distance(Cartesian3 &left, Cartesian3 &right)
			{
				Cartesian3 distanceScratch = Cartesian3::subtract(left, right);
				return Cartesian3::magnitude(distanceScratch);
			};

			/**
			 * Computes the squared distance between two points.  Comparing squared distances
			 * using this function is more efficient than comparing distances using {@link Cartesian3#distance}.
			 *
			 * @param {Cartesian3} left The first point to compute the distance from.
			 * @param {Cartesian3} right The second point to compute the distance to.
			 * @returns {number} The distance between two points.
			 *
			 * @example
			 * // Returns 4.0, not 2.0
			 * const d = Cesium.Cartesian3.distanceSquared(new Cesium.Cartesian3(1.0, 0.0, 0.0), new Cesium.Cartesian3(3.0, 0.0, 0.0));
			 */
			static double distanceSquared(Cartesian3 &left, Cartesian3 &right)
			{
				auto distanceScratch = Cartesian3::subtract(left, right);
				return Cartesian3::magnitudeSquared(distanceScratch);
			};

			/**
			 * Computes the normalized form of the supplied Cartesian.
			 *
			 * @param {Cartesian3} cartesian The Cartesian to be normalized.
			 * @param {Cartesian3} result The object onto which to store the result.
			 * @returns {Cartesian3} The modified result parameter.
			 */
			static Cartesian3 normalize(Cartesian3 cartesian)
			{
				double magnitude = Cartesian3::magnitude(cartesian);
				Cartesian3 result;
				result.x = cartesian.x / magnitude;
				result.y = cartesian.y / magnitude;
				result.z = cartesian.z / magnitude;

				//>>includeStart('debug', pragmas.debug);
				// if (isNaN(result.x) || isNaN(result.y) || isNaN(result.z)) {
				//	throw new DeveloperError("normalized result is not a number");
				//}
				//>>includeEnd('debug');

				return result;
			};

			/**
			 * Computes the dot (scalar) product of two Cartesians.
			 *
			 * @param {Cartesian3} left The first Cartesian.
			 * @param {Cartesian3} right The second Cartesian.
			 * @returns {number} The dot product.
			 */
			static double dot(Cartesian3 &left, Cartesian3 &right)
			{
				return left.x * right.x + left.y * right.y + left.z * right.z;
			};

			/**
			 * Computes the componentwise product of two Cartesians.
			 *
			 * @param {Cartesian3} left The first Cartesian.
			 * @param {Cartesian3} right The second Cartesian.
			 * @param {Cartesian3} result The object onto which to store the result.
			 * @returns {Cartesian3} The modified result parameter.
			 */
			static Cartesian3 multiplyComponents(const Cartesian3 left, const Cartesian3 right)
			{
				Cartesian3 result;
				result.x = left.x * right.x;
				result.y = left.y * right.y;
				result.z = left.z * right.z;
				return result;
			};

			/**
			 * Computes the componentwise quotient of two Cartesians.
			 *
			 * @param {Cartesian3} left The first Cartesian.
			 * @param {Cartesian3} right The second Cartesian.
			 * @param {Cartesian3} result The object onto which to store the result.
			 * @returns {Cartesian3} The modified result parameter.
			 */
			static Cartesian3 divideComponents(const Cartesian3 &left, const Cartesian3 &right)
			{
				Cartesian3 result;

				result.x = left.x / right.x;
				result.y = left.y / right.y;
				result.z = left.z / right.z;
				return result;
			};

			/**
			 * Computes the componentwise sum of two Cartesians.
			 *
			 * @param {Cartesian3} left The first Cartesian.
			 * @param {Cartesian3} right The second Cartesian.
			 * @param {Cartesian3} result The object onto which to store the result.
			 * @returns {Cartesian3} The modified result parameter.
			 */
			static Cartesian3 add(Cartesian3 &left, Cartesian3 right)
			{
				Cartesian3 result;

				result.x = left.x + right.x;
				result.y = left.y + right.y;
				result.z = left.z + right.z;
				return result;
			};

			/**
			 * Computes the componentwise difference of two Cartesians.
			 *
			 * @param {Cartesian3} left The first Cartesian.
			 * @param {Cartesian3} right The second Cartesian.
			 * @param {Cartesian3} result The object onto which to store the result.
			 * @returns {Cartesian3} The modified result parameter.
			 */
			static Cartesian3 subtract(Cartesian3 left, Cartesian3 right)
			{
				Cartesian3 result;
				result.x = left.x - right.x;
				result.y = left.y - right.y;
				result.z = left.z - right.z;
				return result;
			};

			/**
			 * Multiplies the provided Cartesian componentwise by the provided scalar.
			 *
			 * @param {Cartesian3} cartesian The Cartesian to be scaled.
			 * @param {number} scalar The scalar to multiply with.
			 * @param {Cartesian3} result The object onto which to store the result.
			 * @returns {Cartesian3} The modified result parameter.
			 */
			static Cartesian3 multiplyByScalar(Cartesian3 cartesian, double scalar)
			{
				Cartesian3 result;
				result.x = cartesian.x * scalar;
				result.y = cartesian.y * scalar;
				result.z = cartesian.z * scalar;
				return result;
			};

			static Cartesian3 multiplyByScalar(Quaternion cartesian, double scalar);

			/**
			 * Divides the provided Cartesian componentwise by the provided scalar.
			 *
			 * @param {Cartesian3} cartesian The Cartesian to be divided.
			 * @param {number} scalar The scalar to divide by.
			 * @param {Cartesian3} result The object onto which to store the result.
			 * @returns {Cartesian3} The modified result parameter.
			 */
			static Cartesian3 divideByScalar(Cartesian3 &cartesian, double scalar)
			{
				Cartesian3 result;
				result.x = cartesian.x / scalar;
				result.y = cartesian.y / scalar;
				result.z = cartesian.z / scalar;
				return result;
			};

			/**
			 * Negates the provided Cartesian.
			 *
			 * @param {Cartesian3} cartesian The Cartesian to be negated.
			 * @param {Cartesian3} result The object onto which to store the result.
			 * @returns {Cartesian3} The modified result parameter.
			 */
			static Cartesian3 negate(Cartesian3 &cartesian)
			{
				Cartesian3 result;
				result.x = -cartesian.x;
				result.y = -cartesian.y;
				result.z = -cartesian.z;
				return result;
			};

			/**
			 * Computes the absolute value of the provided Cartesian.
			 *
			 * @param {Cartesian3} cartesian The Cartesian whose absolute value is to be computed.
			 * @param {Cartesian3} result The object onto which to store the result.
			 * @returns {Cartesian3} The modified result parameter.
			 */
			static Cartesian3 abs(Cartesian3 &cartesian)
			{
				Cartesian3 result;
				result.x = std::abs(cartesian.x);
				result.y = std::abs(cartesian.y);
				result.z = std::abs(cartesian.z);
				return result;
			};

			/**
			 * Computes the linear interpolation or extrapolation at t using the provided cartesians.
			 *
			 * @param {Cartesian3} start The value corresponding to t at 0.0.
			 * @param {Cartesian3} end The value corresponding to t at 1.0.
			 * @param {number} t The point along t at which to interpolate.
			 * @param {Cartesian3} result The object onto which to store the result.
			 * @returns {Cartesian3} The modified result parameter.
			 */
			static Cartesian3 lerp(Cartesian3 &start, Cartesian3 &end, double t)
			{
				Cartesian3 lerpScratch = Cartesian3::multiplyByScalar(end, t);
				Cartesian3 result = Cartesian3::multiplyByScalar(start, 1.0 - t);
				return Cartesian3::add(lerpScratch, result);
			};

			/**
			 * Returns the angle, in radians, between the provided Cartesians.
			 *
			 * @param {Cartesian3} left The first Cartesian.
			 * @param {Cartesian3} right The second Cartesian.
			 * @returns {number} The angle between the Cartesians.
			 */
			static double angleBetween(Cartesian3 &left, Cartesian3 &right)
			{
				auto angleBetweenScratch = Cartesian3::normalize(left);
				auto angleBetweenScratch2 = Cartesian3::normalize(right);
				auto cosine = Cartesian3::dot(angleBetweenScratch, angleBetweenScratch2);
				auto sine = Cartesian3::magnitude(
					Cartesian3::cross(
						angleBetweenScratch,
						angleBetweenScratch2));
				return atan2(sine, cosine);
			};

			/**
			 * Returns the axis that is most orthogonal to the provided Cartesian.
			 *
			 * @param {Cartesian3} cartesian The Cartesian on which to find the most orthogonal axis.
			 * @param {Cartesian3} result The object onto which to store the result.
			 * @returns {Cartesian3} The most orthogonal axis.
			 */
			static Cartesian3 mostOrthogonalAxis(Cartesian3 &cartesian)
			{
				auto f = Cartesian3::normalize(cartesian);
				f = Cartesian3::abs(f);

				Cartesian3 result;
				if (f.x <= f.y)
				{
					if (f.x <= f.z)
					{
						result = Cartesian3::UNIT_X;
					}
					else
					{
						result = Cartesian3::UNIT_Z;
					}
				}
				else if (f.y <= f.z)
				{
					result = Cartesian3::UNIT_Y;
				}
				else
				{
					result = Cartesian3::UNIT_Z;
				}

				return result;
			};

			/**
			 * Projects vector a onto vector b
			 * @param {Cartesian3} a The vector that needs projecting
			 * @param {Cartesian3} b The vector to project onto
			 * @param {Cartesian3} result The result cartesian
			 * @returns {Cartesian3} The modified result parameter
			 */
			static Cartesian3 projectVector(Cartesian3 &a, Cartesian3 &b)
			{
				double scalar = Cartesian3::dot(a, b) / Cartesian3::dot(b, b);
				return Cartesian3::multiplyByScalar(b, scalar);
			};

			/**
			 * Compares the provided Cartesians componentwise and returns
			 * <code>true</code> if they are equal, <code>false</code> otherwise.
			 *
			 * @param {Cartesian3} [left] The first Cartesian.
			 * @param {Cartesian3} [right] The second Cartesian.
			 * @returns {boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise.
			 */
			static bool equals(const Cartesian3 &left, const Cartesian3 &right)
			{
				Cartesian3 result;
				return (
					left.x == right.x &&
					left.y == right.y &&
					left.z == right.z);
			};

			bool operator==(const Cartesian3 &v) const
			{
				return equals(*this, v);
			}

			bool operator!=(const Cartesian3 &v) const
			{
				return !equals(*this, v);
			}

			Cartesian3 operator+(const Cartesian3 v)
			{
				return Cartesian3::add(*this, v);
			}

			Cartesian3 operator-()
			{
				return Cartesian3(-this->x, -this->y, -this->z);
			}

			Cartesian3 operator-(const Cartesian3 v)
			{
				return Cartesian3::subtract(*this, v);
			}

			Cartesian3 operator*(const float v)
			{
				return Cartesian3::multiplyByScalar(*this, v);
			}

			Cartesian3 operator*(const Cartesian3 v)
			{
				return Cartesian3::multiplyComponents(*this, v);
			}

			Cartesian3 &operator*=(const float v)
			{
				this->x *= v;
				this->y *= v;
				this->z *= v;
				return *this;
			}

			Cartesian3 &operator/=(const Cartesian3 v)
			{
				this->x /= v.x;
				this->y /= v.y;
				this->z /= v.z;
				return *this;
			}

			Cartesian3 &operator/=(const float v)
			{
				this->x /= v;
				this->y /= v;
				this->z /= v;
				return *this;
			}

			Cartesian3 operator/(const float v)
			{
				return Cartesian3::divideByScalar(*this, v);
			}

			Cartesian3 operator/(const Cartesian3 &v)
			{
				return Cartesian3::divideComponents(*this, v);
			}

			Cartesian3 &operator*=(const Cartesian3 v)
			{
				this->x *= v.x;
				this->y *= v.y;
				this->z *= v.z;
				return *this;
			}

			/**
			 * @private
			 */
			static bool equalsArray(Cartesian3 &cartesian, DoubleVector &array1, size_t offset)
			{
				return (
					cartesian.x == array1[offset] &&
					cartesian.y == array1[offset + 1] &&
					cartesian.z == array1[offset + 2]);
			};

			/**
			 * Compares the provided Cartesians componentwise and returns
			 * <code>true</code> if they pass an absolute or relative tolerance test,
			 * <code>false</code> otherwise.
			 *
			 * @param {Cartesian3} [left] The first Cartesian.
			 * @param {Cartesian3} [right] The second Cartesian.
			 * @param {number} [relativeEpsilon=0] The relative epsilon tolerance to use for equality testing.
			 * @param {number} [absoluteEpsilon=relativeEpsilon] The absolute epsilon tolerance to use for equality testing.
			 * @returns {boolean} <code>true</code> if left and right are within the provided epsilon, <code>false</code> otherwise.
			 */
			static bool equalsEpsilon(
				Cartesian3 left,
				Cartesian3 right,
				double relativeEpsilon = 0.0,
				double absoluteEpsilon = 0.0)
			{
				return (
					CesiumMath::equalsEpsilon(
						left.x,
						right.x,
						relativeEpsilon,
						absoluteEpsilon) &&
					CesiumMath::equalsEpsilon(
						left.y,
						right.y,
						relativeEpsilon,
						absoluteEpsilon) &&
					CesiumMath::equalsEpsilon(
						left.z,
						right.z,
						relativeEpsilon,
						absoluteEpsilon));
			};

			/**
			 * Computes the cross (outer) product of two Cartesians.
			 *
			 * @param {Cartesian3} left The first Cartesian.
			 * @param {Cartesian3} right The second Cartesian.
			 * @param {Cartesian3} result The object onto which to store the result.
			 * @returns {Cartesian3} The cross product.
			 */
			static Cartesian3 cross(Cartesian3 &left, Cartesian3 &right)
			{
				double leftX = left.x;
				double leftY = left.y;
				double leftZ = left.z;
				double rightX = right.x;
				double rightY = right.y;
				double rightZ = right.z;

				double x = leftY * rightZ - leftZ * rightY;
				double y = leftZ * rightX - leftX * rightZ;
				double z = leftX * rightY - leftY * rightX;
				Cartesian3 result;
				result.x = x;
				result.y = y;
				result.z = z;
				return result;
			};

			/**
			 * Computes the midpoint between the right and left Cartesian.
			 * @param {Cartesian3} left The first Cartesian.
			 * @param {Cartesian3} right The second Cartesian.
			 * @param {Cartesian3} result The object onto which to store the result.
			 * @returns {Cartesian3} The midpoint.
			 */
			static Cartesian3 midpoint(Cartesian3 &left, Cartesian3 &right)
			{
				Cartesian3 result;

				result.x = (left.x + right.x) * 0.5;
				result.y = (left.y + right.y) * 0.5;
				result.z = (left.z + right.z) * 0.5;

				return result;
			};

			/**
			 * Returns a Cartesian3 position from longitude and latitude values given in degrees.
			 *
			 * @param {number} longitude The longitude, in degrees
			 * @param {number} latitude The latitude, in degrees
			 * @param {number} [height=0.0] The height, in meters, above the ellipsoid.
			 * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the position lies.
			 * @param {Cartesian3} [result] The object onto which to store the result.
			 * @returns {Cartesian3} The position
			 *
			 * @example
			 * const position = Cesium.Cartesian3.fromDegrees(-115.0, 37.0);
			 */
			static Cartesian3 fromDegrees(
				double longitude,
				double latitude,
				double height = 0.0)
			{
				longitude = CesiumMath::toRadians(longitude);
				latitude = CesiumMath::toRadians(latitude);
				return Cartesian3::fromRadians(longitude, latitude, height);
			}

			static const Cartesian3 wgs84RadiiSquared;

			/**
			 * Returns a Cartesian3 position from longitude and latitude values given in radians.
			 *
			 * @param {number} longitude The longitude, in radians
			 * @param {number} latitude The latitude, in radians
			 * @param {number} [height=0.0] The height, in meters, above the ellipsoid.
			 * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the position lies.
			 * @param {Cartesian3} [result] The object onto which to store the result.
			 * @returns {Cartesian3} The position
			 *
			 * @example
			 * const position = Cesium.Cartesian3.fromRadians(-2.007, 0.645);
			 */
			static Cartesian3 fromRadians(
				double longitude,
				double latitude,
				double height = 0.0)
			{
				Cartesian3 radiiSquared = wgs84RadiiSquared;
				Cartesian3 scratchN;
				double cosLatitude = cos(latitude);
				scratchN.x = cosLatitude * cos(longitude);
				scratchN.y = cosLatitude * sin(longitude);
				scratchN.z = sin(latitude);
				scratchN = Cartesian3::normalize(scratchN);

				auto scratchK = Cartesian3::multiplyComponents(radiiSquared, scratchN);
				auto gamma = sqrt(Cartesian3::dot(scratchN, scratchK));
				scratchK = Cartesian3::divideByScalar(scratchK, gamma);
				scratchN = Cartesian3::multiplyByScalar(scratchN, height);

				return Cartesian3::add(scratchK, scratchN);
			};

			/**
			 * Returns an array1 of Cartesian3 positions given an array1 of longitude and latitude values given in degrees.
			 *
			 * @param {number[]} coordinates A list of longitude and latitude values. Values alternate [longitude, latitude, longitude, latitude...].
			 * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the coordinates lie.
			 * @param {Cartesian3[]} [result] An array1 of Cartesian3 objects to store the result.
			 * @returns {Cartesian3[]} The array1 of positions.
			 *
			 * @example
			 * const positions = Cesium.Cartesian3.fromDegreesArray([-115.0, 37.0, -107.0, 33.0]);
			 */
			static std::vector<Cartesian3> fromDegreesArray(DoubleVector coordinates)
			{
				size_t length = coordinates.size();
				std::vector<Cartesian3> result;
				result.resize(length / 2);

				for (auto i = 0; i < length; i += 2)
				{
					double longitude = coordinates[i];
					double latitude = coordinates[i + 1];
					size_t index = i / 2;
					result[index] = Cartesian3::fromDegrees(
						longitude,
						latitude,
						0);
				}

				return result;
			};

			/**
			 * Returns an array1 of Cartesian3 positions given an array1 of longitude and latitude values given in radians.
			 *
			 * @param {number[]} coordinates A list of longitude and latitude values. Values alternate [longitude, latitude, longitude, latitude...].
			 * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the coordinates lie.
			 * @param {Cartesian3[]} [result] An array1 of Cartesian3 objects to store the result.
			 * @returns {Cartesian3[]} The array1 of positions.
			 *
			 * @example
			 * const positions = Cesium.Cartesian3.fromRadiansArray([-2.007, 0.645, -1.867, .575]);
			 */
			static std::vector<Cartesian3> fromRadiansArray(DoubleVector coordinates)
			{
				auto length = coordinates.size();

				std::vector<Cartesian3> result;
				result.resize(length / 2);

				for (auto i = 0; i < length; i += 2)
				{
					auto longitude = coordinates[i];
					auto latitude = coordinates[i + 1];
					auto index = i / 2;
					result[index] = Cartesian3::fromRadians(
						longitude,
						latitude,
						0);
				}

				return result;
			};

			/**
			 * Returns an array1 of Cartesian3 positions given an array1 of longitude, latitude and height values where longitude and latitude are given in degrees.
			 *
			 * @param {number[]} coordinates A list of longitude, latitude and height values. Values alternate [longitude, latitude, height, longitude, latitude, height...].
			 * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the position lies.
			 * @param {Cartesian3[]} [result] An array1 of Cartesian3 objects to store the result.
			 * @returns {Cartesian3[]} The array1 of positions.
			 *
			 * @example
			 * const positions = Cesium.Cartesian3.fromDegreesArrayHeights([-115.0, 37.0, 100000.0, -107.0, 33.0, 150000.0]);
			 */
			static std::vector<Cartesian3> fromDegreesArrayHeights(DoubleVector &coordinates)
			{
				auto length = coordinates.size();
				std::vector<Cartesian3> result;
				result.resize(length / 3);

				for (auto i = 0; i < length; i += 3)
				{
					auto longitude = coordinates[i];
					auto latitude = coordinates[i + 1];
					auto height = coordinates[i + 2];
					auto index = i / 3;
					result[index] = Cartesian3::fromDegrees(
						longitude,
						latitude,
						height);
				}

				return result;
			};

			/**
			 * Returns an array1 of Cartesian3 positions given an array1 of longitude, latitude and height values where longitude and latitude are given in radians.
			 *
			 * @param {number[]} coordinates A list of longitude, latitude and height values. Values alternate [longitude, latitude, height, longitude, latitude, height...].
			 * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the position lies.
			 * @param {Cartesian3[]} [result] An array1 of Cartesian3 objects to store the result.
			 * @returns {Cartesian3[]} The array1 of positions.
			 *
			 * @example
			 * const positions = Cesium.Cartesian3.fromRadiansArrayHeights([-2.007, 0.645, 100000.0, -1.867, .575, 150000.0]);
			 */
			static std::vector<Cartesian3> fromRadiansArrayHeights(DoubleVector &coordinates)
			{
				auto length = coordinates.size();
				std::vector<Cartesian3> result;
				result.resize(length / 3);

				for (auto i = 0; i < length; i += 3)
				{
					auto longitude = coordinates[i];
					auto latitude = coordinates[i + 1];
					auto height = coordinates[i + 2];
					auto index = i / 3;
					result[index] = Cartesian3::fromRadians(
						longitude,
						latitude,
						height);
				}

				return result;
			};

			/**
			 * An immutable Cartesian3 instance initialized to (0.0, 0.0, 0.0).
			 *
			 * @type {Cartesian3}
			 * @constant
			 */
			static const Cartesian3 ZERO;

			/**
			 * An immutable Cartesian3 instance initialized to (1.0, 1.0, 1.0).
			 *
			 * @type {Cartesian3}
			 * @constant
			 */
			static const Cartesian3 ONE;

			/**
			 * An immutable Cartesian3 instance initialized to (1.0, 0.0, 0.0).
			 *
			 * @type {Cartesian3}
			 * @constant
			 */
			static const Cartesian3 UNIT_X;

			/**
			 * An immutable Cartesian3 instance initialized to (0.0, 1.0, 0.0).
			 *
			 * @type {Cartesian3}
			 * @constant
			 */
			static const Cartesian3 UNIT_Y;

			/**
			 * An immutable Cartesian3 instance initialized to (0.0, 0.0, 1.0).
			 *
			 * @type {Cartesian3}
			 * @constant
			 */
			static const Cartesian3 UNIT_Z;

			/**
			 * Compares this Cartesian against the provided Cartesian componentwise and returns
			 * <code>true</code> if they are equal, <code>false</code> otherwise.
			 *
			 * @param {Cartesian3} [right] The right hand side Cartesian.
			 * @returns {boolean} <code>true</code> if they are equal, <code>false</code> otherwise.
			 */
			bool equals(Cartesian3 &right)
			{
				return Cartesian3::equals(*this, right);
			};

			/**
			 * Compares this Cartesian against the provided Cartesian componentwise and returns
			 * <code>true</code> if they pass an absolute or relative tolerance test,
			 * <code>false</code> otherwise.
			 *
			 * @param {Cartesian3} [right] The right hand side Cartesian.
			 * @param {number} [relativeEpsilon=0] The relative epsilon tolerance to use for equality testing.
			 * @param {number} [absoluteEpsilon=relativeEpsilon] The absolute epsilon tolerance to use for equality testing.
			 * @returns {boolean} <code>true</code> if they are within the provided epsilon, <code>false</code> otherwise.
			 */
			bool equalsEpsilon(
				Cartesian3 right,
				double relativeEpsilon,
				double absoluteEpsilon)
			{
				return Cartesian3::equalsEpsilon(
					*this,
					right,
					relativeEpsilon,
					absoluteEpsilon);
			};

			/**
			 * Creates a string representing this Cartesian in the format '(x, y, z)'.
			 *
			 * @returns {string} A string representing this Cartesian in the format '(x, y, z)'.
			 */
			String toString()
			{
				return str_format("%f,%f,%f", x, y, z);
			};
		};
	}
}

#endif
